package com.zff.rental.utils.sql;

import lombok.Data;

import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 条件
 *
 * @author HCY
 */
@Data
public final class Condition<T> {
    private Column column;
    private ConditionOperator operator;
    private T value1;
    private T value2;

    public Condition(Column column, ConditionOperator operator) {
        this.column = column;
        this.operator = operator;
    }

    public Condition(Column column, ConditionOperator operator, T value1) {
        this.column = column;
        this.operator = operator;
        this.value1 = value1;
    }

    public Condition(Column column, ConditionOperator operator, T value1, T value2) {
        this.column = column;
        this.operator = operator;
        this.value1 = value1;
        this.value2 = value2;
    }

    public String toSql() {
        return switch (operator) {
            case EQUAL -> column.toSqlColumn() + " = " + toSqlValue(value1);
            case NOT_EQUAL -> column.toSqlColumn() + " != " + toSqlValue(value1);
            case GREATER_THAN -> column.toSqlColumn() + " > " + toSqlValue(value1);
            case LESS_THAN -> column.toSqlColumn() + " < " + toSqlValue(value1);
            case GREATER_THAN_OR_EQUAL -> column.toSqlColumn() + " >= " + toSqlValue(value1);
            case LESS_THAN_OR_EQUAL -> column.toSqlColumn() + " <= " + toSqlValue(value1);
            case LIKE -> column.toSqlColumn() + " LIKE " + toSqlValue(value1);
            case IN -> column.toSqlColumn() + " IN " + toSqlValue(value1);
            case NOT_IN -> column.toSqlColumn() + " NOT IN " + toSqlValue(value1);
            case BETWEEN -> column.toSqlColumn() + " BETWEEN " + toSqlValue(value1) + " AND " + toSqlValue(value1);
            case NOT_BETWEEN ->
                    column.toSqlColumn() + " NOT BETWEEN " + toSqlValue(value1) + " AND " + toSqlValue(value1);
            case IS_NULL -> column.toSqlColumn() + " IS NULL";
            case IS_NOT_NULL -> column.toSqlColumn() + " IS NOT NULL";
            case NATIVE_SQL -> column.getColumn();
        };
    }

    private static String toSqlValue(Object value) {
        if (value == null) {
            return "";
        } else if (value instanceof Column) {
            return ((Column) value).toSqlColumn();
        } else if (value instanceof Number) {
            return String.valueOf(value);
        } else if (value instanceof String) {
            return "'" + value + "'";
        } else if (value instanceof Collection) {
            return ((Collection<?>) value).stream()
                    .map(Condition::toSqlValue)
                    .collect(Collectors.joining(",", "(", ")"));
        } else if (value instanceof Object[]) {
            return Stream.of((Object[]) value)
                    .map(Condition::toSqlValue)
                    .collect(Collectors.joining(",", "(", ")"));
        } else {
            return "'" + value + "'";
        }
    }

    /**
     * 原始Sql
     */
    public static <T> Condition<T> of(String nativeSql) {
        return new Condition<>(Column.of(nativeSql), ConditionOperator.NATIVE_SQL);
    }

    public static <T> Condition<T> equal(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.EQUAL, value);
    }

    public static <T> Condition<T> equal(Column column, T value) {
        return new Condition<>(column, ConditionOperator.EQUAL, value);
    }

    public static <T> Condition<T> notEqual(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.NOT_EQUAL, value);
    }

    public static <T> Condition<T> notEqual(Column column, T value) {
        return new Condition<>(column, ConditionOperator.NOT_EQUAL, value);
    }

    public static <T> Condition<T> greaterThan(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.GREATER_THAN, value);
    }

    public static <T> Condition<T> greaterThan(Column column, T value) {
        return new Condition<>(column, ConditionOperator.GREATER_THAN, value);
    }

    public static <T> Condition<T> lessThan(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.LESS_THAN, value);
    }

    public static <T> Condition<T> lessThan(Column column, T value) {
        return new Condition<>(column, ConditionOperator.LESS_THAN, value);
    }

    public static <T> Condition<T> greaterThanOrEqual(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.GREATER_THAN_OR_EQUAL, value);
    }

    public static <T> Condition<T> greaterThanOrEqual(Column column, T value) {
        return new Condition<>(column, ConditionOperator.GREATER_THAN_OR_EQUAL, value);
    }

    public static <T> Condition<T> lessThanOrEqual(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.LESS_THAN_OR_EQUAL, value);
    }

    public static <T> Condition<T> lessThanOrEqual(Column column, T value) {
        return new Condition<>(column, ConditionOperator.LESS_THAN_OR_EQUAL, value);
    }

    public static <T> Condition<T> like(String column, T value) {
        return new Condition<>(Column.of(column), ConditionOperator.LIKE, value);
    }

    public static <T> Condition<T> like(Column column, T value) {
        return new Condition<>(column, ConditionOperator.LIKE, value);
    }

    public static <T> Condition<T[]> in(String column, T[] value) {
        return new Condition<>(Column.of(column), ConditionOperator.IN, value);
    }

    public static <T> Condition<T[]> in(Column column, T[] value) {
        return new Condition<>(column, ConditionOperator.IN, value);
    }

    public static <T> Condition<Collection<T>> in(String column, Collection<T> value) {
        return new Condition<>(Column.of(column), ConditionOperator.IN, value);
    }

    public static <T> Condition<Collection<T>> in(Column column, Collection<T> value) {
        return new Condition<>(column, ConditionOperator.IN, value);
    }

    public static <T> Condition<T[]> notIn(String column, T[] value) {
        return new Condition<>(Column.of(column), ConditionOperator.NOT_IN, value);
    }

    public static <T> Condition<T[]> notIn(Column column, T[] value) {
        return new Condition<>(column, ConditionOperator.NOT_IN, value);
    }

    public static <T> Condition<Collection<T>> notIn(String column, Collection<T> value) {
        return new Condition<>(Column.of(column), ConditionOperator.NOT_IN, value);
    }

    public static <T> Condition<Collection<T>> notIn(Column column, Collection<T> value) {
        return new Condition<>(column, ConditionOperator.NOT_IN, value);
    }

    public static <T> Condition<T> between(String column, T value1, T value2) {
        return new Condition<>(Column.of(column), ConditionOperator.BETWEEN, value1, value2);
    }

    public static <T> Condition<T> between(Column column, T value1, T value2) {
        return new Condition<>(column, ConditionOperator.BETWEEN, value1, value2);
    }

    public static <T> Condition<T> notBetween(String column, T value1, T value2) {
        return new Condition<>(Column.of(column), ConditionOperator.NOT_BETWEEN, value1, value2);
    }

    public static <T> Condition<T> notBetween(Column column, T value1, T value2) {
        return new Condition<>(column, ConditionOperator.NOT_BETWEEN, value1, value2);
    }

    public static <T> Condition<T> isNull(String column) {
        return new Condition<>(Column.of(column), ConditionOperator.IS_NULL);
    }

    public static <T> Condition<T> isNull(Column column) {
        return new Condition<>(column, ConditionOperator.IS_NULL);
    }

    public static <T> Condition<T> isNotNull(String column) {
        return new Condition<>(Column.of(column), ConditionOperator.IS_NOT_NULL);
    }

    public static <T> Condition<T> isNotNull(Column column) {
        return new Condition<>(column, ConditionOperator.IS_NOT_NULL);
    }

}
